package cn.hrc.config;


import cn.hrc.utils.JwtUtil;
import cn.hrc.utils.PackJson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/*
* 继承重写 SpringSecurity 已存在的 账号密码 认证过滤器*/
public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter {
    // 日志 记录器
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    private RedisTemplate redisTemplate;
    AuthenticationManager authenticationManager;

    public JwtLoginFilter(RedisTemplate redisTemplate, AuthenticationManager manager) {
        System.out.println("认证管理器："+manager);

        this.authenticationManager = manager;
        this.redisTemplate = redisTemplate;
        // 设置登录地址
        super.setFilterProcessesUrl("/api/login");
    }
    /* 准备去认证 attempt 尝试 Authentication 认证*/
    @Override
    public Authentication attemptAuthentication(
            HttpServletRequest request,
            HttpServletResponse response)
            throws AuthenticationException {
        //获取浏览器登录的账号密码
        String username = obtainUsername(request);
        String pwd = obtainPassword(request);
//        request.getParameter(""); 支持账号登录 ，也支持短信验证码登录
        System.out.println("username:" + username+"\npwd:"+pwd);
        logger.debug("username:"+username+"\t\tpwd:"+pwd);
        // tok 封装用户名密码
        UsernamePasswordAuthenticationToken tok =
                new UsernamePasswordAuthenticationToken(username, pwd);
        logger.info("tok:\t"+tok);
        //让 SpringSecurity 的登录【认证】 管理器去认证
        Authentication authenticate = authenticationManager.authenticate(tok);
        logger.info("认证对象返回=>\t");
        System.out.println("authenticate:\t"+authenticate);
        System.out.println("<=认证对象成功返回");
        System.out.println("【身份】\t"+authenticate.getAuthorities());
        System.out.println("=====================");
        return authenticate;
    }

    @Override
    protected void successfulAuthentication(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain chain,
            Authentication authResult
    ) throws IOException, ServletException {
        logger.info("登录成功");
        logger.info("authenticationManager:【认证成功】");
        //
        User user = (User) authResult.getPrincipal();

        logger.info("jwtUSer:\t"+user);
        logger.info("username:\t"+user.getUsername()+"\t\tpassword:\t"+user.getPassword());
        // 获取用户角色
        List<String> roles = new ArrayList<>();
        //
        for (GrantedAuthority sga : user.getAuthorities()) {
            logger.info("sga:\t"+sga.toString());
            roles.add(sga.toString());
        }
        // 生成 token
        String token = JwtUtil.createToken(user.getUsername(), roles);
        logger.info("token:\t"+token);

        // token 存储到 redis
        String uuid = UUID.randomUUID().toString()
                .replace("-", "");
        // 存储 token 到 redis
        redisTemplate.opsForValue()
                .set("token:"+uuid, // key
                        token,// value
                        1,// 过期时间
                        TimeUnit.DAYS);// 过期时间单位【天】
        // 返回 token
        Map<String, Object> data = new HashMap<>();
        data.put("token",uuid);
        String json = PackJson.packJson(200,"登录成功",data);
        logger.info("json:\t"+json);
        response.setStatus(200);
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(json);// 返回给浏览器
        response.flushBuffer();// 刷新缓冲区

    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request,
                                              HttpServletResponse response,
                                              AuthenticationException failed)
            throws IOException, ServletException {

        System.out.println("authenticationManager:【认证失败】");

        response.setContentType("application/json;charset=UTF-8");

//        response.setContentType("text/html;charset=UTF-8");

        response.setStatus(401); //设置状态码
        /*
        @ResponseBody 实现原理，自己把对象转成JSON字符串，
        再用 response 相应浏览器*/
        String strJson = PackJson.packJson(401,"认证失败",null);

        response.getWriter().write(strJson);
        response.flushBuffer();

    }

}
